/*-
 * Copyright (c) 2007-2008 SINA Corporation, All Rights Reserved.
 * Author: Zhang shuo <zhangshuo@staff.sina.com.cn>
 * A protocol basic processer.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "protocol_basic.h"


static int split_header(PROTOCOL *handler, char *spliter);


/* public functions */

PROTOCOL * 
pb_init(const char *header_term, const char *spliter, BUF_TRANSFER *buf_transfer, 
				P_RESPONSER *p_responser, HEADER_PARSER *header_parser)
{
	PROTOCOL *new_protocol;
	
	new_protocol = (PROTOCOL *)PB_MALLOC(sizeof(PROTOCOL));
	
	if (new_protocol == NULL)
	{
		ERROR_LOG("Not enough memory.");
        return NULL;
	}
	
	new_protocol->cdt_buf_handler = condition_buf_init(header_term);
	new_protocol->len_buf_handler = length_buf_init();
	new_protocol->mc_console = READ_HEADER;
	
	new_protocol->req_entity.entity_entry = NULL;
	new_protocol->req_entity.entity_len = 0;
	
	memset(new_protocol->spliter, 0, MAX_SPLITER_LEN + 1);
	memmove(new_protocol->spliter, spliter, MAX_SPLITER_LEN);
	
	/* store the call back function's pointer */
	if (buf_transfer == NULL || p_responser == NULL || header_term == NULL
		|| header_parser == NULL
		|| new_protocol->cdt_buf_handler == NULL
		|| new_protocol->len_buf_handler == NULL)
	{
		ERROR_LOG("init buffer error.");
		return NULL;
	}
	else
	{
		new_protocol->buf_transfer = buf_transfer;
		new_protocol->p_responser = p_responser;
		new_protocol->header_parser = header_parser;
	}
	
    return new_protocol;
}



int 
protocol_entry(PROTOCOL *handler, char *data, size_t len)
{
	int state;
	int res_cdt;
	int res_len;
	int res_resp;
    size_t stop_len;
	ssize_t header_res;
	
	switch (handler->mc_console)
	{
		case READ_HEADER :
	
			state = READING_HEADER;
			
			res_cdt = condition_buf(handler->cdt_buf_handler, data, len);
			
			#ifdef MC_DEBUG
			printf("conditon buffer resp: %d\n", res_cdt);
			printf("READ_HEADER\n");
			#endif
			
			if (res_cdt != CONTINUE_BUF && res_cdt != NO_DATA)
			{
				if (res_cdt < 0)
				{
					ERROR_LOG("buffer error. condition_buf return %d", res_cdt);
					condition_buf_reset(handler->cdt_buf_handler);
					return ERROR;
				}
				else
				{
					/* the buffer has finished. first we inform the system to read entity
					 * in the buffer. it may be a part of the follwing binary data */
					handler->mc_console = READ_ENTITY; 
					
					/* now here starts to process the header and set req_header */
					if (split_header(handler, handler->spliter) == -1)
					{
						ERROR_LOG("header parsing error.");
						return ERROR;
					}
					else
					{
						/* parse the header and get the entity data stop length */
						header_res = ((HEADER_PARSER *)handler->header_parser)(handler);

                        if (header_res < 0)
                            return ERROR;
                        else if (header_res == 0)
                            stop_len = 0;
                        else
                            stop_len = (size_t)header_res;

						/* set the stop length */
						length_buf_set_sl(handler->len_buf_handler, stop_len);
					}
						
					
					#ifdef MC_DEBUG
					printf("--> now the len buffer stop length is:%ld\n", 
									handler->len_buf_handler->buf_length);
					#endif
					
					/* save the remained data to fixed length buffer */
					res_len = length_buf(handler->len_buf_handler, 
							cdt_get_remain_data(handler->cdt_buf_handler), 
							cdt_get_remain_len(handler->cdt_buf_handler));
							
					#ifdef MC_DEBUG
					printf("entity1:%s\n", len_get_buf(handler->len_buf_handler));
					printf("length buffer resp: %d\n", res_len);
					printf("bytes_read:%ld\n", handler->len_buf_handler->bytes_read);
					#endif
					
					if (res_len != CONTINUE_BUF)
					{
						if (res_len == FULL)
						{
							ERROR_LOG("fixed-length-buffer is too small to hold the data sent every time.");
							return ERROR;
						}
						else
						{
							/* call the buffer transfer funciton to copy the data to another place */
							handler->req_entity.entity_entry = 
								handler->buf_transfer(len_get_buf(handler->len_buf_handler), len_get_data_len(handler->len_buf_handler));
							
							handler->req_entity.entity_len += len_get_data_len(handler->len_buf_handler);
							
							res_resp = ((P_RESPONSER *)handler->p_responser)(handler);
							
							/* reset 'basic_protocol' for next command */
							//protocol_reset(handler);
							
							if (res_resp == P_RESPONSER_ERR)
								return ERROR;
							else
                            {
                                handler->mc_console = END;
								return READING_FINISH;
                            }
						}
					}
				}
			}
			
			break;
			
		case READ_ENTITY :
	
			state = READING_ENTITY;
			
			res_len = length_buf(handler->len_buf_handler, data, len);
			
			#ifdef MC_DEBUG
			printf("READ_ENTITY\n");
			printf("length buffer resp: %d\n", res_len);
			printf("entity2:\n%s\n", len_get_buf(handler->len_buf_handler));
			printf("bytes_read:%ld\n", handler->len_buf_handler->bytes_read);
			#endif
			
			if (res_len != CONTINUE_BUF)
			{
				if (res_len == FULL) 
				{
					/* call the buffer transfer funciton to copy the data to another place */
					handler->req_entity.entity_entry = 
						handler->buf_transfer(len_get_buf(handler->len_buf_handler), len_get_data_len(handler->len_buf_handler));
						
					handler->req_entity.entity_len += len_get_data_len(handler->len_buf_handler);
					
					return LEN_BUF_AGAIN;
				}
				else
				{
					/* call the buffer transfer funciton to copy the data to another place */
					handler->req_entity.entity_entry = 
						handler->buf_transfer(len_get_buf(handler->len_buf_handler), len_get_data_len(handler->len_buf_handler));

					
					handler->req_entity.entity_len += len_get_data_len(handler->len_buf_handler);
					
					res_resp = ((P_RESPONSER *)handler->p_responser)(handler);
					
					/* reset 'basic_protocol' for next command */
					//protocol_reset(handler);
					
					if (res_resp == P_RESPONSER_ERR)
						return ERROR;
					else
                    {
                        handler->mc_console = END;
						return READING_FINISH;
                    }
				}
			}	
			
			break;
	}
	
	return state;
}


int 
protocol_reset(PROTOCOL *handler)
{
	/* reset the buffer */
	condition_buf_reset(handler->cdt_buf_handler);
	length_buf_reset(handler->len_buf_handler);
	
	/* reset basic_protocol itself */
	handler->req_entity.entity_entry = NULL;
	handler->req_entity.entity_len = 0;
	handler->mc_console = READ_HEADER;
	
	handler->req_header.fields_count = 0;

	handler->user_data = NULL;
	handler->custom_p1 = NULL;
	handler->custom_p2 = NULL;
	handler->custom_p3 = NULL;
	handler->custom_p4 = NULL;

	return 1;
}

int 
protocol_clean(PROTOCOL *handler)
{
	if (handler->cdt_buf_handler == NULL 
        || handler->len_buf_handler == NULL)
    {
        return 0;
    }
    
    
    /* clean the buffer */
	condition_buf_clean(handler->cdt_buf_handler);
	length_buf_clean(handler->len_buf_handler);
	
	/* clean the struct */
    PB_FREE(handler);
	
	return 1;
}


static int 
split_header(PROTOCOL *handler, char *spliter)
{
	char *buffer;
	char **ap;
	
	
	ap = handler->req_header.fields;
	
	/* get the buffer pointer */
	buffer = cdt_get_buf(handler->cdt_buf_handler);
	
	/* add '\0' at the end of buffer */
	buffer[cdt_get_data_len(handler->cdt_buf_handler)] = '\0';
	
	handler->req_header.fields_count = 0;
	
	/* use strsep to separate the string with ' ' token */
	for ( ; (*ap = mc_strsep(&buffer, spliter)) != NULL; )
	{
		handler->req_header.fields_count++;
		if (**ap != '\0')
		{
			if (++ap >= &handler->req_header.fields[MAX_FIELDS])break;
		}
	}
                   
	#ifdef MC_DEBUG
		int i;
		
		for (i=0; i < handler->req_header.fields_count; i++)
		{
			printf("field[%d]: %s\n",i,handler->req_header.fields[i]);
		}
		printf("field is:%d\n", handler->req_header.fields_count);
	#endif
	
	return 0;
}

